﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace RapidRepository.View
{
    /// <summary>
    /// Class for storing view information.
    /// </summary>
    public class RapidViewData<TEntity, TView> : IRapidViewDataInternal, IRapidViewData<TEntity, TView>
        where TEntity : IRapidEntity
        where TView : IRapidView
    {
        #region Declarations

        static object syncRoot = new object();
        Func<TEntity, TView> mapViewExpression;
        List<Func<TView, bool>> filterExpressions;
        List<object> view;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the type of the view.
        /// </summary>
        /// <value>The type of the view.</value>
        public Type ViewType
        {
            get { return typeof(TView); }
        }

        /// <summary>
        /// Gets the type of the entity.
        /// </summary>
        /// <value>The type of the entity.</value>
        public Type EntityType
        {
            get { return typeof(TEntity); }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="RapidViewData&lt;TEntity, TView&gt;"/> is loaded.
        /// </summary>
        /// <value><c>true</c> if loaded; otherwise, <c>false</c>.</value>
        public bool IsLoaded { get; set; }

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="RapidViewData&lt;TEntity, TView&gt;"/> class.
        /// </summary>
        /// <param name="mapViewExpression">The map view expression.</param>
        public RapidViewData(Func<TEntity, TView> mapViewExpression)
        {
            Contract.Requires("mapViewExpression", mapViewExpression != null);

            this.mapViewExpression = mapViewExpression;
            this.filterExpressions = new List<Func<TView, bool>>();
            this.view = new List<object>();
        } 

        #endregion

        #region Add Filter

        /// <summary>
        /// Adds a filter expression.
        /// </summary>
        /// <param name="filterExpression">The filter expression.</param>
        /// <returns></returns>
        public IRapidViewData<TEntity, TView> AddFilter(Func<TView, bool> filterExpression)
        {
            Contract.Requires("filterExpression", filterExpression != null);

            this.filterExpressions.Add(filterExpression);
            return this;
        } 

        #endregion

        #region InsertView

        /// <summary>
        /// Inserts the view.
        /// </summary>
        /// <param name="view">The view.</param>
        public void InsertView(List<object> view)
        {
            Contract.Requires("view", view != null);
            lock (syncRoot)
            {
                this.view = view;
            }
        }

        #endregion

        #region LoadView

        /// <summary>
        /// Loads the view.
        /// </summary>
        public List<object> LoadView()
        {
            lock (syncRoot)
            {
                List<object> validViews = new List<object>();
                foreach (var view in this.view)
                {
                    bool isValid = true;

                    foreach (var filter in this.filterExpressions)
                    {                        
                        if (!filter.Invoke((TView)view))
                        {
                            isValid = false;
                        }
                    }

                    if (isValid)
                    {
                        validViews.Add(view);
                    }                    
                }
                this.view = validViews;

                return this.view;
            }
        }

        #endregion

        #region Add

        /// <summary>
        /// Tries the add.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public void Add(object entity)
        {
            Contract.Requires("entity", entity != null);

            bool isValid = true;
            TView newViewItem = this.mapViewExpression.Invoke((TEntity)entity);
            newViewItem.Id = ((IRapidEntity)entity).Id;
            foreach (var filter in this.filterExpressions)
            {
                if (!filter(newViewItem))
                {
                    isValid = false;
                }
            }
            if (isValid)
            {
                lock (syncRoot)
                {
                    this.view.Add(newViewItem);
                }
            }
        }

        #endregion

        #region Update

        /// <summary>
        /// Tries the add.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public void Update(object entity)
        {
            Contract.Requires("entity", entity != null);

            this.Delete(((IRapidEntity)entity).Id);

            this.Add(entity);
        } 

        #endregion

        #region Delete

        /// <summary>
        /// Tries the add.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public void Delete(Guid entityId)
        {
            Contract.Requires("entityId", entityId != Guid.Empty);

            object currentView = null;

            lock (syncRoot)
            {
                currentView = this.view.Where(x => ((TView)x).Id == entityId).FirstOrDefault();
            }

            if (currentView == null)
            {
                return;
            }

            lock (syncRoot)
            {
                this.view.Remove(currentView);
            }
        }

        #endregion
    }
}
